package com.company.nuwa.redis;

import cn.hutool.core.util.ObjectUtil;
import com.company.nuwa.redis.common.RedisConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

/**
 * <p>enable caching redis cache config</p >
 *
 * @author Nikola Tesla
 * @version 1.0
 * @date 2021/1/14 10:21
 */
@Configuration(proxyBeanMethods = false)
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({RedissonClientConfiguration.class})
public class RedisCacheManagerConfiguration {

    @Bean
    public RedisConfiguration redisConfiguration(CacheProperties cacheProperties, @Value("${spring.application.name}") String applicationName) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        // Key prefix.
        String prefixKeys = ObjectUtil.defaultIfNull(redisProperties.getKeyPrefix(), applicationName + ":");
        // Entry expiration. By default the entries never expire.
        Duration ttl = ObjectUtil.defaultIfNull(redisProperties.getTimeToLive(), Duration.ofSeconds(60 * 60));

        RedisConfiguration configuration = new RedisConfiguration()
                .setKeyPrefix(prefixKeys)
                .setTimeToLive(ttl)
                .setCacheNullValues(redisProperties.isCacheNullValues())
                .setUseKeyPrefix(redisProperties.isUseKeyPrefix())
                .setObjectCacheRedisSerializer(new GenericJackson2JsonRedisSerializer());

        Logger.log.info("======> spring redis configuration , prefix keys={}, ttl={} <======", prefixKeys, ttl);

        return configuration;
    }

    @Bean
    @Primary
    public CacheManager redisCacheManager(RedisConfiguration redisConfiguration, RedissonConnectionFactory connectionFactory) {

        org.springframework.data.redis.cache.RedisCacheConfiguration defaultCacheConfig = createConfiguration(redisConfiguration);

        RedisCacheManager.RedisCacheManagerBuilder redisCacheManagerBuilder = RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(connectionFactory)
                .cacheDefaults(defaultCacheConfig);

        return redisCacheManagerBuilder.build();
    }

    private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(RedisConfiguration redisConfiguration) {
        org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisConfiguration.getObjectCacheRedisSerializer()))
                .entryTtl(redisConfiguration.getTimeToLive())
                .prefixCacheNameWith(redisConfiguration.getKeyPrefix());

        // Allow caching null values.
        if (!redisConfiguration.isCacheNullValues()) {
            config = config.disableCachingNullValues();
            Logger.log.warn("======> ${spring.cache.redis.cache-null-values} is false, not recommended. <======");
        }
        // Whether to use the key prefix when writing to Redis.
        if (!redisConfiguration.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
            Logger.log.warn("======> ${spring.cache.redis.use-key-prefix} is false, not recommended. <======");
        }

        return config;
    }

    @Slf4j
    static class Logger {
    }

}
